use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthRequest {
    pub auth: String,
    pub rx: u64,
    #[serde(skip_serializing_if = "String::is_empty", default)]
    pub padding: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthResponse {
    pub udp_enabled: bool,
    pub rx: u64,
    pub rx_auto: bool,
    #[serde(skip_serializing_if = "String::is_empty", default)]
    pub padding: String,
}

impl AuthRequest {
    pub fn new(auth: String, rx: u64) -> Self {
        Self {
            auth,
            rx,
            padding: String::new(),
        }
    }

    pub fn to_headers(&self) -> Vec<(String, String)> {
        vec![
            ("hysteria-auth".to_string(), self.auth.clone()),
            ("hysteria-cc-rx".to_string(), self.rx.to_string()),
            ("hysteria-padding".to_string(), self.padding.clone()),
        ]
    }

    pub fn from_headers(headers: &http::HeaderMap) -> Result<Self> {
        let auth = headers
            .get("hysteria-auth")
            .and_then(|v| v.to_str().ok())
            .ok_or(Error::MissingAuthHeader)?
            .to_string();

        let rx = headers
            .get("hysteria-cc-rx")
            .and_then(|v| v.to_str().ok())
            .and_then(|v| v.parse().ok())
            .unwrap_or(0);

        let padding = headers
            .get("hysteria-padding")
            .and_then(|v| v.to_str().ok())
            .unwrap_or("")
            .to_string();

        Ok(Self { auth, rx, padding })
    }
}

impl AuthResponse {
    pub fn new(udp_enabled: bool, rx: u64, rx_auto: bool) -> Self {
        Self {
            udp_enabled,
            rx,
            rx_auto,
            padding: String::new(),
        }
    }

    pub fn to_headers(&self) -> Vec<(String, String)> {
        let rx_value = if self.rx_auto {
            "auto".to_string()
        } else {
            self.rx.to_string()
        };

        vec![
            ("hysteria-udp".to_string(), self.udp_enabled.to_string()),
            ("hysteria-cc-rx".to_string(), rx_value),
            ("hysteria-padding".to_string(), self.padding.clone()),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_auth_request() {
        let req = AuthRequest::new("password123".to_string(), 1000000);
        let headers = req.to_headers();
        
        assert_eq!(headers.len(), 3);
        assert_eq!(headers[0].0, "hysteria-auth");
        assert_eq!(headers[0].1, "password123");
    }

    #[test]
    fn test_auth_response() {
        let resp = AuthResponse::new(true, 2000000, false);
        let headers = resp.to_headers();
        
        assert_eq!(headers[0].1, "true");
        assert_eq!(headers[1].1, "2000000");
    }
}
